// Copyright (C) 2010  Joan Puigcerver Perez, <joapuipe@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include <People.hpp>

#include <iostream>
#include <fstream>
#include <cstring>
#include <typeinfo>

#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>

#include <SAXPeopleHandler.hpp>

People::People(std::ostream& logf, ost::Mutex& logm)
  : logfile(logf), log_mutex(logm), num_persons(0)
{
  
}


People::~People()
{
  for(std::vector<Person*>::iterator it = people_container.begin();
      it!=people_container.end();++it)
    delete *it;
}

bool People::load(Database * database)
{
  /*
  try {
    XMLPlatformUtils::Initialize();
  } 
  catch( const XMLException & toCatch ) {
    char* message = XMLString::transcode(toCatch.getMessage());
    print_log_line("Error during initialization! :", logfile, log_mutex);
    print_log_line("Exception message is:", logfile, log_mutex);
    print_log_line(message, logfile, log_mutex);
    XMLString::release(&message);
    return false;
    }*/
  
  SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();
  
  SAXPeopleHandler handler(database, logfile, log_mutex);
  parser->setContentHandler(&handler);
  parser->setErrorHandler(&handler);
  
  try {
    parser->parse(PEOPLE_XML);
  }
  catch (const XMLException& toCatch) {
    char* message = XMLString::transcode(toCatch.getMessage());
    print_log_line("Exception message is:", logfile, log_mutex);
    print_log_line(message, logfile, log_mutex);
    XMLString::release(&message);
    delete parser;
    //XMLPlatformUtils::Terminate();
    return false;
  }
  catch (const SAXParseException& toCatch) {
    char* message = XMLString::transcode(toCatch.getMessage());
    print_log_line("Exception message is:", logfile, log_mutex);
    print_log_line(message, logfile, log_mutex);;
    XMLString::release(&message);
    delete parser;
    //XMLPlatformUtils::Terminate();
    return false;
  }
  catch (const OutOfMemoryException& toCatch) {
    char *message = XMLString::transcode(toCatch.getMessage());
    print_log_line("Exception message is:", logfile, log_mutex);
    print_log_line(message, logfile, log_mutex);
    XMLString::release(&message);
    delete parser;
    //XMLPlatformUtils::Terminate();
    return false;
  }
  catch (...) {
    print_log_line("Unexpected exception!", logfile, log_mutex);
    if ( handler.curr_person == NULL ) {
      print_log_line("Null person!", logfile, log_mutex);
    } else {
      print_log_line(handler.curr_person->name(), logfile, log_mutex);
    }
    delete parser;
    //XMLPlatformUtils::Terminate();
    return false;
  }
  
  delete parser;
  //XMLPlatformUtils::Terminate();
  
  return true;
}

Person * People::add_person(const std::string& name)
{
  if ( people.exact_lookup(name) == NULL ) {
    Person * person = new Person(num_persons, name);
    people_container.push_back(person);
    if ( people.insert_main(person->name(), person) ){ num_persons++; return person; }
  } 
  return NULL;
}

void People::add_aka(const std::string& aka, Person* person)
{
  people.insert_aka(person->add_aka(aka), person);
}

//std::pair< bool,std::list< std::pair<const Person*, std::string> > >
People::Tsearch
People::search(const std::string& name) const
{
  const Person * p = people.exact_lookup(name);
  std::list< std::pair<const Person*, std::string> > res_p;
  if ( p != NULL ) {
    res_p.push_back( std::pair<const Person*, std::string>(p,name) );
    return Tsearch(true, res_p);
  }
  
  size_t maxerrs = std::min((size_t)4, name.length()/4);
  std::vector<ApproximateLookupItem> res_n = people.approximate_lookup(name, maxerrs);

  // eliminem repeticions
  std::sort(res_n.begin(), res_n.end());
  std::vector<ApproximateLookupItem>::iterator it = std::unique(res_n.begin(), res_n.end());
  res_n.resize(it - res_n.begin() );
  
  bool found = false;
  for(std::vector<ApproximateLookupItem>::const_iterator it = res_n.begin();
      it != res_n.end(); ++it)
    {
      if ( it->second == name ) found = true;
      res_p.push_back( (*it) );
    }
  return Tsearch(found, res_p);
}

size_t People::size(void) const
{
  return num_persons;
}
